﻿#include "MonsterInfo.h"
#include "../Settings.h"

using namespace Server::MirEnvir;

namespace Server::MirDatabase
{

    Server::MirEnvir::Envir *MonsterInfo::getEnvir()
    {
        return Envir::getMain();
    }

    Server::MirEnvir::Envir *MonsterInfo::getEditEnvir()
    {
        return getEnvir()->Edit;
    }

    Server::MessageQueue *MonsterInfo::getMessageQueue()
    {
        return MessageQueue::getInstance();
    }

    MonsterInfo::MonsterInfo()
    {
        Stats = new Stats();
    }

    MonsterInfo::MonsterInfo(BinaryReader *reader)
    {
        Index = reader->ReadInt32();
        Name = reader->ReadString();

        Image = static_cast<Monster>(reader->ReadUInt16());
        AI = reader->ReadByte();
        Effect = reader->ReadByte();

        if (Envir::LoadVersion < 62)
        {
            Level = static_cast<unsigned short>(reader->ReadByte());
        }
        else
        {
            Level = reader->ReadUInt16();
        }

        ViewRange = reader->ReadByte();
        CoolEye = reader->ReadByte();

        if (Envir::LoadVersion > 84)
        {
            Stats = new Stats(reader);
        }

        if (Envir::LoadVersion <= 84)
        {
            Stats = new Stats();
            Stats[Stat::HP] = static_cast<int>(reader->ReadUInt32()); //Monster form prevented greater than ushort, so this should never overflow.
        }

        if (Envir::LoadVersion < 62)
        {
            Stats[Stat::MinAC] = reader->ReadByte();
            Stats[Stat::MaxAC] = reader->ReadByte();
            Stats[Stat::MinMAC] = reader->ReadByte();
            Stats[Stat::MaxMAC] = reader->ReadByte();
            Stats[Stat::MinDC] = reader->ReadByte();
            Stats[Stat::MaxDC] = reader->ReadByte();
            Stats[Stat::MinMC] = reader->ReadByte();
            Stats[Stat::MaxMC] = reader->ReadByte();
            Stats[Stat::MinSC] = reader->ReadByte();
            Stats[Stat::MaxSC] = reader->ReadByte();
        }
        else
        {
            if (Envir::LoadVersion <= 84)
            {
                Stats[Stat::MinAC] = reader->ReadUInt16();
                Stats[Stat::MaxAC] = reader->ReadUInt16();
                Stats[Stat::MinMAC] = reader->ReadUInt16();
                Stats[Stat::MaxMAC] = reader->ReadUInt16();
                Stats[Stat::MinDC] = reader->ReadUInt16();
                Stats[Stat::MaxDC] = reader->ReadUInt16();
                Stats[Stat::MinMC] = reader->ReadUInt16();
                Stats[Stat::MaxMC] = reader->ReadUInt16();
                Stats[Stat::MinSC] = reader->ReadUInt16();
                Stats[Stat::MaxSC] = reader->ReadUInt16();
            }
        }

        if (Envir::LoadVersion <= 84)
        {
            Stats[Stat::Accuracy] = reader->ReadByte();
            Stats[Stat::Agility] = reader->ReadByte();
        }

        Light = reader->ReadByte();

        AttackSpeed = reader->ReadUInt16();
        MoveSpeed = reader->ReadUInt16();

        Experience = reader->ReadUInt32();

        CanPush = reader->ReadBoolean();
        CanTame = reader->ReadBoolean();

        if (Envir::LoadVersion < 18)
        {
            return;
        }
        AutoRev = reader->ReadBoolean();
        Undead = reader->ReadBoolean();
    }

    std::string MonsterInfo::getGameName() const
    {
        return Regex::Replace(Name, R"([\d-])", "");
    }

    void MonsterInfo::Save(BinaryWriter *writer)
    {
        writer->Write(Index);
        writer->Write(Name);

        writer->Write(static_cast<unsigned short>(Image));
        writer->Write(AI);
        writer->Write(Effect);
        writer->Write(Level);
        writer->Write(ViewRange);
        writer->Write(CoolEye);

        Stats->Save(writer);


        writer->Write(Light);

        writer->Write(AttackSpeed);
        writer->Write(MoveSpeed);

        writer->Write(Experience);

        writer->Write(CanPush);
        writer->Write(CanTame);
        writer->Write(AutoRev);
        writer->Write(Undead);
    }

    void MonsterInfo::LoadDrops()
    {
        Drops.clear();
        std::string path = FileSystem::combine(Settings::DropPath, Name + ".txt");
        if (!FileSystem::fileExists(path))
        {
            std::vector<std::string> contents = {";Pots + Other", "", "", ";Weapons", "", "", ";Armour", "", "", ";Helmets", "", "", ";Necklace", "", "", ";Bracelets", "", "", ";Rings", "", "", ";Shoes", "", "", ";Belts", "", "", ";Stone"};

            File::WriteAllLines(path, contents);


            return;
        }

        std::vector<std::string> lines = File::ReadAllLines(path);

        for (int i = 0; i < lines.size(); i++)
        {
            if (StringHelper::startsWith(lines[i], ";") || StringHelper::isEmptyOrWhiteSpace(lines[i]))
            {
                continue;
            }

            DropInfo *drop = DropInfo::FromLine(lines[i]);
            if (drop == nullptr)
            {
                getMessageQueue()->Enqueue(StringHelper::formatSimple("Could not load Drop: {0}, Line {1}", Name, lines[i]));
                continue;
            }

            Drops.push_back(drop);
        }

//C# TO C++ CONVERTER TODO TASK: The 'Compare' parameter of std::sort produces a boolean value, while the .NET Comparison parameter produces a tri-state result:
//ORIGINAL LINE: Drops.Sort((drop1, drop2) =>
        std::sort(Drops.begin(), Drops.end(), [&] (drop1, drop2)
        {
                if (drop1::Gold > 0 && drop2->Gold == 0)
                {
                    return 1;
                }
                if (drop1->Gold == 0 && drop2::Gold > 0)
                {
                    return -1;
                }
    
                return drop1::Item->Type->CompareTo(drop2::Item->Type);
        });
    }

    void MonsterInfo::FromText(const std::string &text)
    {
        std::vector<std::string> data = text.Split({','}, StringSplitOptions::RemoveEmptyEntries);

        if (data.size() < 28)
        {
            return; //28
        }

        MonsterInfo *info = new MonsterInfo();
        info->Name = data[0];
        unsigned short image;
        if (!unsigned short::TryParse(data[1], image))
        {
            delete info;
            return;
        }
        info->Image = static_cast<Monster>(image);

        if (!unsigned char::TryParse(data[2], info->AI))
        {
            delete info;
            return;
        }
        if (!unsigned char::TryParse(data[3], info->Effect))
        {
            delete info;
            return;
        }
        if (!unsigned short::TryParse(data[4], info->Level))
        {
            delete info;
            return;
        }
        if (!unsigned char::TryParse(data[5], info->ViewRange))
        {
            delete info;
            return;
        }

        //if (!int.TryParse(data[6], out info.HP)) return;

        //if (!ushort.TryParse(data[7], out info.MinAC)) return;
        //if (!ushort.TryParse(data[8], out info.MaxAC)) return;
        //if (!ushort.TryParse(data[9], out info.MinMAC)) return;
        //if (!ushort.TryParse(data[10], out info.MaxMAC)) return;
        //if (!ushort.TryParse(data[11], out info.MinDC)) return;
        //if (!ushort.TryParse(data[12], out info.MaxDC)) return;
        //if (!ushort.TryParse(data[13], out info.MinMC)) return;
        //if (!ushort.TryParse(data[14], out info.MaxMC)) return;
        //if (!ushort.TryParse(data[15], out info.MinSC)) return;
        //if (!ushort.TryParse(data[16], out info.MaxSC)) return;
        //if (!byte.TryParse(data[17], out info.Accuracy)) return;
        //if (!byte.TryParse(data[18], out info.Agility)) return;
        if (!unsigned char::TryParse(data[19], info->Light))
        {
            delete info;
            return;
        }

        if (!unsigned short::TryParse(data[20], info->AttackSpeed))
        {
            delete info;
            return;
        }
        if (!unsigned short::TryParse(data[21], info->MoveSpeed))
        {
            delete info;
            return;
        }

        if (!unsigned int::TryParse(data[22], info->Experience))
        {
            delete info;
            return;
        }

        if (!bool::TryParse(data[23], info->CanTame))
        {
            delete info;
            return;
        }
        if (!bool::TryParse(data[24], info->CanPush))
        {
            delete info;
            return;
        }

        if (!bool::TryParse(data[25], info->AutoRev))
        {
            delete info;
            return;
        }
        if (!bool::TryParse(data[26], info->Undead))
        {
            delete info;
            return;
        }
        if (!unsigned char::TryParse(data[27], info->CoolEye))
        {
            delete info;
            return;
        }

        //int count;

        //if (!int.TryParse(data[27], out count)) return;

        //if (28 + count * 3 > data.Length) return;

        setEditEnvir(getEditEnvir() + 1);
        info->Index = getEditEnvir()->MonsterIndex;
        getEditEnvir()->MonsterInfoList.push_back(info);

//C# TO C++ CONVERTER TODO TASK: A 'delete info' statement was not added since info was passed to a method or constructor. Handle memory management manually.
    }

    std::string MonsterInfo::ToText()
    {
        return ""; // string.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18},{19},{20},{21},{22},{23},{24},{25},{26},{27}", Name, (ushort)Image, AI, Effect, Level, ViewRange,
          //  HP, 
            //MinAC, MaxAC, MinMAC, MaxMAC, MinDC, MaxDC, MinMC, MaxMC, MinSC, MaxSC, 
           // Accuracy, Agility, Light, AttackSpeed, MoveSpeed, Experience, CanTame, CanPush, AutoRev, Undead, CoolEye);
    }

    std::string MonsterInfo::ToString()
    {
        return StringHelper::formatSimple("{0}: {1}", Index, Name);
        //return string.Format("{0}", Name);
    }

    Server::MirEnvir::Envir *DropInfo::getEnvir()
    {
        return Envir::getMain();
    }

    DropInfo *DropInfo::FromLine(const std::string &s)
    {
        std::vector<std::string> parts = s.Split({' '}, StringSplitOptions::RemoveEmptyEntries);

        if (parts.size() < 2)
        {
            return nullptr;
        }

        DropInfo *info = new DropInfo();

        if (!int::TryParse(parts[0].substr(2), info->Chance))
        {
            delete info;
            return nullptr;
        }
//C# TO C++ CONVERTER TODO TASK: The following System.String compare method is not converted:
        if (std::string::Compare(parts[1], "Gold", StringComparison::OrdinalIgnoreCase) == 0)
        {
            if (parts.size() < 3)
            {
                delete info;
                return nullptr;
            }
            if (!unsigned int::TryParse(parts[2], info->Gold) || info->Gold == 0)
            {
                delete info;
                return nullptr;
            }
        }
        else
        {
            info->Item = getEnvir()->GetItemInfo(parts[1]);
            if (info->Item == nullptr)
            {
                delete info;
                return nullptr;
            }

            if (parts.size() > 2)
            {
                std::string dropRequirement = parts[2];
                if (StringHelper::toUpper(dropRequirement) == "Q")
                {
                    info->QuestRequired = true;
                }
            }
        }

//C# TO C++ CONVERTER TODO TASK: A 'delete info' statement was not added since info was used in a 'return' or 'throw' statement.
        return info;
    }
}
